<?php

/**
 * This file is part of the Codizer package.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright   Copyright (c) 2010 Advisa (http://www.advisa.fr)
 * @author      Codizer Core Team <team@codizer.org>
 * @package     Codizer
 */

class f_document_Loader
{
	/*
	 * @var f_document_QueryBuilder
	 */
	private $queryBuilder;
	private $modelName;
	
	private function __construct($modelName)
	{
		$this->modelName = $modelName;
		$this->queryBuilder = f_document_QueryBuilder::getInstance()->initByModel($modelName);
	}
	
	/**
	 * @return f_document_Loader
	 */
	public static function getInstance($modelName)
	{
		return new self($modelName);
	}
	
// ================================================= GENERIC METHODS TO SELECT QUERY	
	
	/**
	 * Defines field and value to add to request
	 *
	 * @param string $fieldName
	 * @param mixed $value
	 * @return f_document_Loader
	 */
	public function whereIsEqual($fieldName, $value)
	{
		$this->queryBuilder->addEqualCondition($fieldName, $value);
		return $this;
	}

	/**
	 * Defines field and value to add to request
	 *
	 * @param string $fieldName
	 * @param mixed $value
	 * @return f_document_Loader
	 */
	public function orIsEqual($fieldName, $value)
	{
		$this->queryBuilder->addEqualCondition($fieldName, $value, 'OR');
		return $this;
	}
	
	/**
	 * Defines field and value to add to request
	 *
	 * @param string $fieldName
	 * @param mixed $value
	 * @return f_document_Loader
	 */
	public function whereIsNotEqual($fieldName, $value)
	{
		$this->queryBuilder->addNotEqualCondition($fieldName, $value);
		return $this;
	}	
	
	/**
	 * Defines field and value to add to request
	 *
	 * @param string $fieldName
	 * @param mixed $value
	 * @return f_document_Loader
	 */
	public function whereIsLessThan($fieldName, $value)
	{
		$this->queryBuilder->addLessThanCondition($fieldName, $value);
		return $this;
	}	

	/**
	 * Defines field and value to add to request
	 *
	 * @param string $fieldName
	 * @param mixed $value
	 * @return f_document_Loader
	 */
	public function whereIsMoreThan($fieldName, $value)
	{
		$this->queryBuilder->addMoreThanCondition($fieldName, $value);
		return $this;
	}	
	
	/**
	 * Defines field and value to add to request
	 *
	 * @param string $fieldName
	 * @param mixed $value
	 * @return f_document_Loader
	 */
	public function whereIsLessOrEqual($fieldName, $value)
	{
		$this->queryBuilder->addLessThanOrEqualCondition($fieldName, $value);
		return $this;
	}	
	
	/**
	 * Defines field and value to add to request
	 *
	 * @param string $fieldName
	 * @param mixed $value
	 * @return f_document_Loader
	 */
	public function whereIsMoreOrEqual($fieldName, $value)
	{
		$this->queryBuilder->addMoreThanOrEqualCondition($fieldName, $value);
		return $this;
	}		
	
	/**
	 * Defines field and value to add to request
	 *
	 * @param string $fieldName
	 * @param mixed $value
	 * @return f_document_Loader
	 */
	public function whereLike($fieldName, $value)
	{
		$this->queryBuilder->addLikeCondition($fieldName, $value);
		return $this;
	}

	/**
	 * Defines field and value to add to request
	 *
	 * @param string $fieldName
	 * @param mixed $value
	 * @return f_document_Loader
	 */
	public function orLike($fieldName, $value)
	{
		$this->queryBuilder->addLikeCondition($fieldName, $value, 'OR');
		return $this;
	}
	
	/**
	 * Defines field and value to add to request
	 *
	 * @param integer $parentId
	 * @param string $relationType
     * @param boolean $getRelationFields Set to true if you have a field in your table with a f_relation field name
	 * @return f_document_Loader
	 */
	public function isChildOf($parentId, $relationType = null, $getRelationFields = false)
	{
		$this->queryBuilder->addChildOfRelationCondition($parentId, $relationType, $getRelationFields);
		return $this;
	}
	
	/**
	 * Defines field and value to add to request
	 *
	 * @param integer $childId
	 * @param string $relationType
     * @param boolean $getRelationFields Set to true if you have a field in your table with a f_relation field name
	 * @return f_document_Loader
	 */
	public function isParentOf($childId, $relationType = null, $getRelationFields = false)
	{
		$this->queryBuilder->addParentOfRelationCondition($childId, $relationType, $getRelationFields);
		return $this;
	}
	
	/**
	 * Defines field and value to add to request
	 *
	 * @param string $dateFieldName
	 * @param date $dateBegin
	 * @param date $dateEnd
	 * @return f_document_Loader
	 */
	public function whereDateIsBetween($dateFieldName, $dateBegin = null, $dateEnd = null)
	{
		if(($dateBegin == "" || is_null($dateBegin)) && ($dateEnd == "" || is_null($dateEnd)))
		{
			return $this;
		}
		else if(($dateBegin == "" || is_null($dateBegin)) && is_string($dateEnd))
		{
			 $this->queryBuilder->addDateBeforeCondition($dateFieldName, $dateEnd);
		}
		else if(is_string($dateBegin) && ($dateEnd == "" || is_null($dateEnd))) 
		{
			$this->queryBuilder->addDateAfterCondition($dateFieldName, $dateBegin);
		}
		else if (is_string($dateBegin) && is_string($dateEnd))
		{
			$this->queryBuilder->addDateBetweenCondition($dateFieldName, $dateBegin, $dateEnd);	
		}
		
		return $this;
	}

    /**
     * Get document(s) where date is after given date
     *
     * @param string $dateFieldName
     * @param date $date
     */
    public function whereDateIsAfter($dateFieldName, $date)
    {
        $this->queryBuilder->addDateAfterCondition($dateFieldName, $date);
        return $this;
    }

    /**
     * Get document(s) where date is before given date
     *
     * @param string $dateFieldName
     * @param date $date
     */
    public function whereDateIsBefore($dateFieldName, $date)
    {
        $this->queryBuilder->addDateBeforeCondition($dateFieldName, $date);
        return $this;
    }
	
	/**
	 * Get document(s) where current date is between start and end publication dates.
	 *
	 * @return f_document_Loader
	 */
	public function whereIsPublicationDatesValid()
	{
		$this->queryBuilder->addPublicationDatesCheck();
		return $this;
	}
	
	/**
	 * Get document(s) where start publication date is after current date
	 * 
	 * @return f_document_Loader
	 */
	public function whereIsToCome($fieldName = 'document_startpublicationdate')
	{
		$this->queryBuilder->addCheckToCome($fieldName);
		return $this;		
	}
	
	/**
	 * Get document(s) where end publication date is before current date
	 * 
	 * @return f_document_Loader
	 */	
	public function whereIsPast($fieldName = 'document_endpublicationdate')
	{
		$this->queryBuilder->addCheckPast($fieldName);
		return $this;		
	}

	public function whereRegexp($fieldName, $regexp)
	{
		$this->queryBuilder->addRegexp($fieldName, $regexp);
		return $this;
	}

	/**
	 * Group answers by a field name
	 *
	 * @param string $fieldName
	 * @return f_document_Loader
	 */
	public function groupBy($fieldName)
	{
		if(is_string($fieldName))
		{
			$fieldName = array($fieldName);
		}
		$this->queryBuilder->addGroupBy($fieldName);
		return $this;
	}

	/**
	 * Put DISTINCT on the request fields
	 *
	 * @return f_document_Loader
	 */
	public function distinct()
	{
		$this->queryBuilder->addDist();
		return $this;
	}

// ================================================= METHODS FOR DOCUMENTS	
	
	/**
	 * Retrieve by publication status
	 *
	 * @param string $publicationStatus
	 * @return f_document_Loader
	 */
	public function byPublicationStatus($publicationStatus)
	{
		$publicationStatus = strtoupper($publicationStatus);
		return $this->whereIsEqual("document_publicationstatus", $publicationStatus);
	}
	
	/**
	 * Retrieve documents by author
	 *
	 * @param string $author
	 * @return f_document_Loader
	 */
	public function byAuthor($author)
	{
		return $this->whereIsEqual("document_author", $author);
	}
	
// ================================================= ORDER

	/**
	 * Order results by fieldName desc
	 *
	 * @param string $fieldName
	 * @return f_document_Loader
	 */
	public function addDescOrder($fieldName)
	{
		$this->queryBuilder->addDescOrder($fieldName);
		return $this;
	}
	
	/**
	 * Order results by fieldName asc
	 *
	 * @param string $fieldName
	 * @return f_document_Loader
	 */
	public function addAscOrder($fieldName)
	{
		$this->queryBuilder->addAscOrder($fieldName);
		return $this;
	}
	
// ================================================= LIMIT

	/**
	 * Limit results by quantity, starting at offser
	 * @param integer $nbResults
	 * @param integer $offset
	 * @return f_document_Loader
	 */
	public function limit($nbResults, $offset = 0)
	{
		$this->queryBuilder->addLimit($nbResults, $offset);
		return $this;
	}
	
// ================================================= QUERY BUILDING	
	
	/**
	 * Retrieve only the first document
	 *
	 * @return object
	 */
	public function retrieveFirstDocument($lang = null)
	{
		$this->limit(1);
		$documentIds = $this->retrieveIds();

		$dbProvider = f_document_Provider::getInstance();
		$class = $dbProvider->resolveClassName($this->modelName);

		if(isset($documentIds[0]))
		{
			return call_user_func(array($class, 'getInstanceByUniqueId'), $documentIds[0], $lang);
		}
		
		return null;
	}
	
	/**
	 * Retrieve documents
	 *
	 * @return array
	 */
	public function retrieveDocuments($lang = null)
	{	
		$results = array();
		$documentIds = $this->retrieveIds($lang);
		$dbProvider = f_document_Provider::getInstance();
		$class = $dbProvider->resolveClassName($this->modelName);

		if(isset($documentIds[0]))
		{
            $documentIds = array_unique($documentIds);

			foreach($documentIds as $documentId)
			{
                if($lang == '*')
                {
                    $doc = call_user_func(array($class, 'getInstanceByUniqueId'), $documentId);
                    if($doc->isLocalized())
                    {
                        $original = $doc->getVoDocument();
                        $results[] = $original;

                        $translationLangs = $original->getTranslationLangs();
                        if(count($translationLangs) > 0)
                        {
                            foreach($translationLangs as $translationLang)
                            {
                                $results[] = $doc->getTranslation($translationLang);
                            }
                        }
                    }
                    else
                    {
                        $results[] = $doc;
                    }
                }
                else
                {
                    $results[] = call_user_func(array($class, 'getInstanceByUniqueId'), $documentId, $lang);
                }
			}
		}
		return $results;
	}
	
	/**
	 * Retrieve document_uid values
	 *
	 * @return array
	 */
	public function retrieveIds($lang = null)
	{
		return $this->retrieveColumnValues("document_uid", PDO::FETCH_COLUMN, $lang);
	}
	
	/**
	 * Retrieve document_uid values
	 *
	 * @param array $columns
	 * @param PDO $fetchMode
	 * @return array
	 */
	public function retrieveColumnValues($columns, $fetchMode = PDO::FETCH_NUM, $lang = null)
	{
		$dbProvider = f_database_Provider::getInstance();
		
		if(is_array($columns))
		{
			foreach($columns as $column)
			{
				$this->queryBuilder->addColumnToGet($column);
			}			
		}
		else
		{
			$this->queryBuilder->addColumnToGet($columns);
		}
				
		$tmp = explode("_",$this->queryBuilder->getClassName());
		$model =  "modules_".$tmp[1]."/".$tmp[3];
		
		unset($tmp);
		
		$this->queryBuilder->addEqualCondition("document_model",$model);
		
		$sql = $this->queryBuilder->getQuery($lang);
		$values = $this->queryBuilder->getValues();

		return $dbProvider->setQuery($sql)->execute($values)->getQueryResults($fetchMode);
	}
	
	/**
	 * Count the number of results
	 * @return integer
	 */
	public function count($lang = null, $distinct = true)
	{
        $string = $distinct == true ? "COUNT(DISTINCT document_uid)" : "COUNT(*)";
		$res = $this->retrieveColumnValues($string, PDO::FETCH_COLUMN, $lang);
		return intval($res[0]);
	}
	
	/**
	 * Delete documents
	 *
	 */	
	public function deleteDocuments($lang = null)
	{
		$this->deletingDocuments(false, $lang);
	}
	
	/**
	 * Delete documents in cascading mod 
	 *
	 */		
	public function deleteCascadingDocuments($lang = null)
	{
		$this->deletingDocuments(true, $lang);
	}
	
	/**
	 * Delete documents in $cascading mod (true or false)
	 *
	 */	
	private function deletingDocuments($cascading = false, $lang = null)
	{
		$results = array();
		
		$documentIds = $this->retrieveIds($lang);

		$dbProvider = f_document_Provider::getInstance();
		$class = $dbProvider->resolveClassName($this->modelName);
		
		if(isset($documentIds[0]))
		{
			foreach($documentIds as $documentId)
			{				
				$doc = call_user_func(array($class, 'getInstanceByUniqueId'), $documentId, $lang);
				
				if($cascading == true)
				{
					$doc->deleteCascade();
				}
				else
				{
					$doc->delete();
				}
			}
		}
	}
}